' Simple Garage (Garage manager program for Stunts)
' Copyright (C) 2022 Lucas Pedrosa

' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, version 3 of the License.

' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.

' You should have received a copy of the GNU General Public License
' along with this program.  If not, see <http://www.gnu.org/licenses/>.

' COMPILATION:
' Simple Garage has been compiled with FreeBasic 1.09.1 and tested
' in GNU/Linux Mint, FreeDOS and DOSBox
' It should compile well with any newer version and likely, with some
' older versions. You can get FreeBasic at:
' - http://www.freebasic.net
' For GNU/Linux and for DOS, compile with:
' - fbc sgar.bas
' For Windows, compile with:
' - fbc -s gui sgar.bas

'Simple Garage program
'Version 2 started 2022-08-27

#define SGVERSION "2.0"
#define MAXCARSPERGARAGE 100

#include "file.bi"
#ifdef __FB_LINUX__
	#define SLASH "/"
#else
	#define SLASH "\"
#endif

Type Car
	id As String
	file(1 To 9) As String
	numfiles As Byte
	carname As String
	version As String
	author As String
End Type

Type Group
	gname As String
	cars As String
End Type

Type Garage
	dir As String
	name As String
End Type

Dim Shared car(0 To 15, 1 To MAXCARSPERGARAGE) As Car, cars(0 To 15) As UByte
Dim Shared garage(0 To 15) As Garage, garages As UByte
Dim Shared group(1 To 50) As Group, groups As UByte, locked As String
Dim Shared progdir As String, cfgfile As String
Dim Shared release_date As String, copyright_notice As String
Dim Shared lastcommand(1 To 10) As String


Sub LoadConfig
	garage(0).dir = "." : garage(1).dir = "garage"
	locked = "coun" : garages = 1
	progdir = ExePath
	If Right(progdir, 1) <> SLASH Then progdir &= SLASH
	cfgfile = progdir & "sgar.cfg"
	If Not FileExists(progdir & "sgar.cfg") Then Exit Sub
	
	Dim f As Integer, s As String
	Dim p As String, v As String, n As Short
	
	f = FreeFile
	Open progdir & "sgar.cfg" For Input As f
	Line Input #f, s
	If LCase(Left(s, 4)) = "cfg=" Then
		cfgfile = Mid(s, 5)
		Close f
		If FileExists(Mid(s, 5)) Then
			Open Mid(s, 5) For Input As f
		Else
			'Redirection failed. No such configuration file.
			Exit Sub
		End If
	Else
		Seek #f, 1
		cfgfile = progdir & "sgar.cfg"
	End If
	
	garages = 0 : groups = 0
	
	While Not EoF(f)
		Line Input #f, s
		n = InStr(s, "=")
		If n Then
			p = LCase(Trim(Left(s, n - 1)))
			v = Trim(Mid(s, n + 1))
			
			Select Case p
				Case "stunts", "root"
					garage(0).dir = v
					garage(0).name = "Stunts"
				Case "garage"
					garages += 1
					n = InStr(v, ":")
					If n Then
						garage(garages).dir = Mid(v, n + 1)
						garage(garages).name = Left(v, n - 1)
					Else
						garage(garages).dir = v
						garage(garages).name = ""
					End If
				Case "junkyard"
					garages += 1
					garage(garages).dir = v
					garage(garages).name = "junkyard"
				Case "locked"
					locked = LCase(Trim(v))
				Case "group"
					n = InStr(v, ":")
					If n Then
						groups += 1
						group(groups).gname = LCase(Trim(Left(v, n - 1)))
						group(groups).cars = LCase(Trim(Mid(v, n + 1)))					
					End If
			End Select
		End If
	WEnd
	Close f
	
	'This will default to a garage named "garage" under the current path
	'which may or may not work!
	If garages = 0 Then garages = 1
End Sub


Sub SaveConfig
	Dim f As Integer
	
	f = FreeFile
	Open cfgfile For Output As f
	If Err Then	Exit Sub	'Wrong file name. Can't create
	
	Print #f, "stunts=" & garage(0).dir
	For i As Short = 1 To garages
		If Len(garage(i).name) Then
			Print #f, "garage=" & garage(i).name & ":" & garage(i).dir
		Else
			Print #f, "garage=" & garage(i).dir
		End If
	Next i
	Print #f, "locked=" & locked
	For i As Short = 1 To groups
		Print #f, "group=" & group(i).gname & ":" & group(i).cars
	Next i
	Close f
End Sub


Sub ReadDirs
	Dim As String s, t, id
	Dim code As Byte
	
	For collection As Byte = 0 To garages
		#ifdef __FB_LINUX__
			s = Dir(garage(collection).dir & SLASH & "*")
		#else
			s = Dir(garage(collection).dir & SLASH & "*.*")
		#endif
		While Len(s)
			t = LCase(s) : code = 0
			If Left(t, 3) = "car" AndAlso Right(t, 4) = ".res" AndAlso Len(t) = 11 Then
				'Res file
				id = Mid(t, 4, 4)
				code = 1
			ElseIf Left(t, 4) = "stda" AndAlso Right(t, 4) = ".vsh" AndAlso Len(t) = 12 Then
				'Uncompressed stda file
				id = Mid(t, 5, 4)
				code = 3
			ElseIf Left(t, 4) = "stda" AndAlso Right(t, 4) = ".pvs" AndAlso Len(t) = 12 Then
				'Compressed stda file
				id = Mid(t, 5, 4)
				code = 4
			ElseIf Left(t, 4) = "stdb" AndAlso Right(t, 4) = ".vsh" AndAlso Len(t) = 12 Then
				'Uncompressed stdb file
				id = Mid(t, 5, 4)
				code = 5
			ElseIf Left(t, 4) = "stdb" AndAlso Right(t, 4) = ".pvs" AndAlso Len(t) = 12 Then
				'Compressed stdb file
				id = Mid(t, 5, 4)
				code = 6
			ElseIf Left(t, 2) = "st" AndAlso Right(t, 4) = ".3sh" AndAlso Len(t) = 10 Then
				'Uncompressed shape file
				id = Mid(t, 3, 4)
				code = 7
			ElseIf Left(t, 2) = "st" AndAlso Right(t, 4) = ".p3s" AndAlso Len(t) = 10 Then
				'Compressed shape file
				id = Mid(t, 3, 4)
				code = 8
			ElseIf Left(t, 3) = "car" AndAlso Right(t, 4) = ".cfg" AndAlso Len(t) = 11 Then
				'Car configuration file
				id = Mid(t, 4, 4)
				code = 9
			End If
			
			If code Then
				Dim q As Byte = 0
				
				For i As Short = 1 To cars(collection)
					If car(collection, i).id = id Then
						q = -1
						car(collection, i).file(code) = s
						'Don't include the configuration file in the count
						'so it's easier to recognise redundant files
						If code <> 9 Then car(collection, i).numfiles += 1
						Exit For
					End If
				Next i
				
				If q = 0 Then
					cars(collection) += 1
					car(collection, cars(collection)).id = id
					car(collection, cars(collection)).numfiles = 1
					For i As Byte = 1 To 9
						car(collection, cars(collection)).file(i) = ""
					Next i
					car(collection, cars(collection)).file(code) = s
				End If
			End If
		
			s = Dir
		WEnd
	Next collection
End Sub


Sub ExtractCarInfo (c As Car, path As String)
	If Len(c.file(1)) = 0 Then
		c.carname = "Unknown"
	Else
		Dim f As Integer, s As String, n As Short, l As Long
		
		f = FreeFile
		Open path & SLASH & c.file(1) For Binary Access Read As f
		s = Space(100)
		Get #f, , s
		n = InStr(s, "gnam")
		Get #f, n + 16, l
		l += 39
		s = Space(255)
		Get #f, l, s
		n = InStr(s, Chr(0))
		If n Then s = Left(s, n - 1) Else s = Left(s, 40)
		Close f
		
		c.carname = s
	End If
	
	c.author = "" : c.version = ""
	If Len(c.file(9)) <> 0 Then
		Dim f As Integer, s As String, n As Short
		
		f = FreeFile
		Open path & SLASH & c.file(9) For Input As f
		While Not EoF(f)
			Line Input #f, s
			n = InStr(s, "=")
			If n Then
				Select Case LCase(Trim(Left(s, n - 1)))
					Case "version", "ver"
						c.version = Trim(Mid(s, n + 1))
					Case "author"
						c.author = Trim(Mid(s, n + 1))
				End Select
			End If			
		WEnd
		Close f
	End If
End Sub


Function Resolve(s As String) As String
	'Obtain a list of cars from a search string that can
	'be a car, a list of cars, a group or a filter
	Dim n As Short, t As String, ss As String
	Dim result As String
	
	n = InStr(s, ",")
	If n Then	'It's a list
		Dim temp As String
		
		t = s
		Do
			n = InStr(t, ",")
			If n Then
				ss = Left(t, n - 1)
				t = Mid(t, n + 1)
			Else
				ss = t
				t = ""
			End If
			
			temp = Resolve(ss)
			If Len(temp) Then
				If Len(result) Then result &= ","
				result &= temp
			End If
		Loop Until Len(t) = 0
	ElseIf Left(s, 1) = "#" Then	'It's a group
		For i As Short = 1 To groups
			If LCase(group(i).gname) = LCase(Mid(s, 2)) Then Return group(i).cars
		Next i
	ElseIf Len(s) = 4 AndAlso InStr(s, "*") = 0 AndAlso InStr(s, ",") = 0 Then	'It's a car
		Return LCase(s)
	Else	'It's a filter
		Dim filtype As Byte, q As Byte
		
		If s = "*" Then
			filtype = 0
		ElseIf Left(s, 1) = "*" AndAlso Right(s, 1) = "*" Then
			filtype = 1
			ss = LCase(Mid(s, 2, Len(s) - 2))
		ElseIf Left(s, 1) = "*" Then
			filtype = 2
			ss = LCase(Mid(s, 2))
		ElseIf Right(s, 1) = "*" Then
			filtype = 3
			ss = LCase(Left(s, Len(s) - 1))
		Else
			filtype = 1
			ss = LCase(s)
		End If
		
		For g As Short = 0 To garages
			For i As Short = 1 To cars(g)
				q = 0
				Select Case filtype
					Case 0 : q = -1		'All
					Case 1
						If InStr(LCase(car(g, i).carname), ss) _
							OrElse InStr(LCase(car(g, i).id), ss) Then q = -1
					Case 2
						If Right(LCase(car(g, i).carname), Len(ss)) = ss _
							OrElse Right(LCase(car(g, i).id), Len(ss)) = ss Then q = -1
					Case Else
						If Left(LCase(car(g, i).carname), Len(ss)) = ss _
							OrElse Left(LCase(car(g, i).id), Len(ss)) = ss Then q = -1
				End Select
			
				If q <> 0 AndAlso InStr(result, LCase(car(g, i).id)) = 0 Then 'Add car to the list
					If Len(result) Then result &= ","
					result &= LCase(car(g, i).id)
				End If
			Next i
		Next g
	End If

	'Get rid of repeated cars
	t = result
	result = ""
	Do
		n = InStr(t, ",")
		If n Then
			ss = Left(t, n - 1)
			t = Mid(t, n + 1)
		Else
			ss = t
			t = ""
		End If
		
		If InStr(result, ss) = 0 Then
			If Len(result) Then result &= ","
			result &= ss
		End If
	Loop Until Len(t) = 0
	
	Return result
End Function


Function MoveCar (id As String, orig As UByte, dest As UByte) As Byte
	Dim n As Short, many As Byte
	
	If Left(id, 1) = "#" Then	'It's a referenced group
		many = 1
	ElseIf InStr(id, ",") Then	'It's an ad-hoc group
		many = 2
	Else						'It's an individual car
		many = 0
	End If
	
	If many Then
		'This is a group. Call repeatedly for each car
		Dim s As String, ercount As Short
		Dim thiscar As String, gn As Short = 0
		
		If many = 1 Then
			For i As Short = 1 To groups
				If group(i).gname = LCase(Mid(id, 2)) Then
					gn = i
					Exit For
				End If
			Next i
			
			If gn = 0 Then Return 1	'Group not found
		
			s = group(gn).cars
		Else
			s = id
		End If
		
		Do
			n = InStr(s, ",")
			
			If n Then
				thiscar = LCase(RTrim(Left(s, n - 1)))
				s = LTrim(Mid(s, n + 1))
			Else
				thiscar = LCase(Trim(s))
				s = ""
			End If
			
			ercount += MoveCar(thiscar, orig, dest)
		Loop Until Len(s) = 0
		
		If ercount Then Return 2 Else Return 0
	End If
	
	'If car is locked, it cannot be moved
	If InStr(LCase(locked), LCase(id)) Then Return 3
	
	
	'Find car
	n = 0
	For i As Short = 1 To cars(orig)
		If LCase(car(orig, i).id) = LCase(id) Then
			n = i
			Exit For
		End If
	Next i
	
	If n = 0 Then Return 1	'Car not found
	
	'Move files
	For i As Short = 1 To 8
		If Len(car(orig, n).file(i)) Then
			FileCopy garage(orig).dir & SLASH & car(orig, n).file(i), garage(dest).dir & SLASH & car(orig, n).file(i)
			Kill garage(orig).dir & SLASH & car(orig, n).file(i)
		End If
	Next i
	
	'Move car in lists
	cars(dest) += 1
	car(dest, cars(dest)) = car(orig, n)
	For i As Short = n To cars(orig)
		car(orig, i) = car(orig, i + 1)
	Next i
	cars(orig) -= 1
		
	Return 0	'Executed successfully
End Function


Sub Init
	LoadConfig
	If garage(0).dir = garage(1).dir Then
		Print
		Print "Error: Stunts and garage directory are the same or undefined!"
		Print "       Edit sgar.cfg to set them correctly."
		Print
		End
	End If
	
	Dim f As Integer
	
	f = FreeFile
	Open garage(0).dir & SLASH & "test981" For Output As f : Close f
	If FileExists(garage(0).dir & SLASH & "test981") Then
		Kill garage(0).dir & SLASH & "test981"
	Else
		Print
		Print "Error: Stunts directory not properly configured"
		Print "       Edit sgar.cfg to select the right directory for it"
		Print
		End
	End If
	Open garage(1).dir & SLASH & "test981" For Output As f : Close f
	If FileExists(garage(1).dir & SLASH & "test981") Then
		Kill garage(1).dir & SLASH & "test981"
	Else
		MkDir garage(1).dir
		Open garage(1).dir & SLASH & "test981" For Output As f : Close f
		If FileExists(garage(1).dir & SLASH & "test981") Then
			Kill garage(1).dir & SLASH & "test981"
			Print
			Print "Created garage directory at " & garage(1).dir
			Print
		Else
			Print
			Print "Error: The current garage directory is not accessible and could"
			Print "       not be created. Please edit sgar.cfg to select a garage"
			Print "       directory that can be used"
			Print
			End
		End If
	End If
	
	ReadDirs

	For g As Byte = 0 To garages
		For i As Short = 1 To cars(g)
			ExtractCarInfo car(g, i), garage(g).dir
		Next i
	Next g
End Sub


Function GarageNumber(gname As String) As Byte
	If ValInt(gname) >= 1 Then Return ValInt(gname)
	
	For i As Short = 1 To garages
		If LCase(garage(i).name) = LCase(gname) Then Return i
	Next i
End Function


Sub Echo (s As String = "", eol As Byte = -1)
	If eol Then
		Print s
	Else
		Print s;
	End If
End Sub


Sub RunCommand (s As String)
	Dim As String c, p(1 To 7), t
	Dim n As Short, ps As Byte
	
	n = InStr(s, " ")
	If n Then
		c = LCase(Trim(Left(s, n - 1)))
		t = Trim(Mid(s, n + 1))
		For i As Byte = 1 To 7
			ps += 1
			n = InStr(t, " ")
			If n Then
				p(ps) = RTrim(Left(t, n - 1))
				t = LTrim(Mid(t, n))
			Else
				p(ps) = t
				Exit For
			End If
		Next i
	Else
		c = LCase(Trim(s))
		ps = 0
	End If
	
	Select Case c
		Case ""
		Case "addto"
			Dim thisgroup As Short, st As String, ss As String
			
			If ps <> 2 Then
				Echo "Use: addto <group> <carid>"
				Echo
				Exit Sub
			End If
			
			For i As Short = 1 To groups
				If group(i).gname = LCase(p(1)) Then
					thisgroup = i
					Exit For
				End If
			Next i
			
			st = Resolve(p(2))
			
			Do
				n = InStr(st, ",")
				If n Then
					ss = Left(st, n - 1)
					st = Mid(st, n + 1)
				Else
					ss = st
					st = ""
				End If
				
				If Len(ss) <> 4 Then
					Echo "invalid car id: " & ss
				ElseIf thisgroup = 0 Then
					groups += 1
					group(groups).gname = LCase(p(1))
					group(groups).cars = LCase(ss)
					Echo "Created group " & p(1) & " with car " & ss
					thisgroup = groups
				ElseIf InStr(LCase(group(thisgroup).cars), LCase(ss)) Then
					Echo "Car " & ss & " is already present in group " & p(1)
				Else
					group(thisgroup).cars &= "," & LCase(ss)
					Echo "Added " & ss & " to " & p(1)
				End If
			Loop Until Len(st) = 0
			Echo ""
			SaveConfig
		Case "bring", "retrieve"
			Dim n As Short, t As String, tt As String, c As Short
			Dim g As Short
			
			If ps = 0 OrElse ps > 3 Then
				Echo "Use: retrieve|bring <carID> [[from] <garage>]"
				Echo ""
			Else
				If LCase(p(2)) = "from" Then p(2) = p(3)
				g = 0
				If ps > 1 Then	'Specified garage
					g = GarageNumber(p(2))
					If g > garages Then
						Echo "Only " & garages & " garages available!"
						Echo ""
						Exit Sub
					End If
				End If
				
				t = Resolve(p(1))
				Do
					c = InStr(t, ",")
					If c Then
						tt = Left(t, c - 1)
						t = Mid(t, c + 1)
					Else
						tt = t
						t = ""
					End If
				
					If g Then
						n = MoveCar(tt, g, 0)
					Else
						For i As Short = 1 To garages
							If LCase(garage(i).name) <> "junkyard" Then
								n = MoveCar(tt, i, 0)
								If n = 0 Then Exit For
							End If
						Next i
					End If
					Select Case n
						Case 0 : Echo "Successfully retrieved " & tt
						Case 1 : Echo "Car not found: " & tt
						Case 2 : Echo "Some cars retrieved"
						Case 3 : Echo "Car is locked and cannot be retrieved: " & tt
						Case Else : Echo "Unknown error"
					End Select
				Loop Until Len(t) = 0
				Echo ""
			End If
		Case "car"
			If ps <> 1 Then
				Echo "Use: car <car>"
				Echo ""
				Exit Sub
			End If
			
			p(1) = Resolve(p(1))
			If InStr(p(1), ",") Then
				Echo "Only a single car ID allowed for this command"
				Echo
				Exit Sub
			End If
			
			For g As Byte = 0 To garages
				For i As Short = 1 To cars(g)
					If LCase(car(g, i).id) = LCase(p(1)) Then
						Echo "Car name: " & car(g, i).carname
						If Len(car(g, i).version) Then Echo "Version:  " & car(g, i).version
						If Len(car(g, i).author) Then Echo "Author:   " & car(g, i).author
						Echo "Car ID:   " & UCase(car(g, i).id)
						Echo "Found in: ", 0
						If Len(garage(g).name) Then Echo garage(g).name Else Echo "Garage #" & g
						Echo "Groups:   ", 0
						Dim q As Short = 0
						For j As Short = 1 To groups
							If InStr(LCase(group(j).cars), LCase(car(g, i).id)) Then
								If q Then Echo ", ", 0
								q += 1
								Echo group(j).gname, 0
							End If
						Next j
						If q = 0 Then Echo "None" Else Echo
						Echo "Files:    " & car(g, i).numfiles
						For j As Byte = 1 To 9
							If Len(car(g, i).file(j)) Then
								Echo "  - " & car(g, i).file(j), 0
								Select Case Right(LCase(car(g, i).file(j)), 4)
									Case ".pvs", ".p3s", ".pre" : Echo " (packed)"
									Case Else : Echo " (unpacked)"
								End Select
							End If
						Next j
						Echo
						Exit Sub
					End If
				Next i
			Next g
			
			Echo "Car not found: " & p(1)
			Echo
		Case "cars"
			Dim total As Short
			
			Echo cars(0) & " cars ready for Stunts"
			For i As Short = 1 To garages
				Echo cars(i) & " cars in ", 0
				If Len(garage(i).name) Then Echo garage(i).name Else Echo "garage #" & i
				total += cars(i)
			Next i
			Echo total & " cars parked"
			Echo cars(0) + total & " total cars"
			If Len(locked) Then
				Dim t As String, tn As Short, num As Short
				
				t = locked
				Do
					tn = InStr(t, ",")
					If tn Then
						num += 1
						t = Mid(t, tn + 1)
					Else
						num += 1
						Exit Do
					End If
				Loop
				Echo num & " locked"
			End If
			Echo
		Case "dirs"
			For i As Short = 0 To garages
				If Len(garage(i).name) Then Echo garage(i).name, 0 Else Echo "Garage #" & i, 0
				Echo ": " & garage(i).dir
			Next i
			Echo
		Case "garagedir", "gdir"
			If Len(p(1)) = 0 Then
				Echo "Use: garagedir [<garage>] <directory>"
				Echo
				Exit Sub
			End If
			
			Dim gd As String, gn As Byte
			
			If ps = 1 Then
				gd = p(1)
				gn = 1
			Else
				gd = p(2)
				gn = 0
			End If
			
			If Open(gd & SLASH & "a123.tmp", For Output, As 1) Then
				Echo "Invalid path: " & gd
				Echo
			Else
				Close 1
				Kill gd & SLASH & "a123.tmp"
				
				If gn Then
					garage(1).dir = gd
				Else
					gn = 0
					If ValInt(p(1)) Then
						gn = ValInt(p(1))
						If gn > garages Then gn = 0
					Else
						For i As Short = 1 To garages
							If LCase(garage(i).name) = LCase(p(1)) Then
								gn = i
								Exit For
							End If
						Next i
					End If
					
					If gn Then
						garage(gn).dir = gd
					ElseIf garages < 15 Then
						garages += 1 : gn = garages
						garage(garages).dir = gd
						garage(garages).name = ""
					End If
				End If
				
				SaveConfig
				For i As Short = 0 To garages
					cars(i) = 0
				Next i
				garages = 1
				Init
				Echo "New garage #" & gn & " directory is " & gd
				Echo
			End If
		Case "garagename", "gname"
			If Len(p(1)) = 0 Then
				Echo "Use: garagename <garage> [new_name]"
				Echo
				Exit Sub
			End If
			
			If ps = 1 Then p(2) = ""
			If ValInt(p(1)) > garages Then
				Echo "There's no garage #" & ValInt(p(1)) & ". Only " & garages & " exist!"
				Echo
				Exit Sub
			End If
			
			If ValInt(p(1)) = 0 Then
				For i As Short = 1 To garages
					If LCase(garage(i).name) = LCase(p(1)) Then
						p(1) = Str(i)
						Exit For
					End If
				Next i
			End If
			
			If ValInt(p(1)) Then
				garage(ValInt(p(1))).name = p(2)
				SaveConfig
				If Len(p(2)) Then
					Echo "Garage #" & ValInt(p(1)) & " renamed as " & p(2)
				Else
					Echo "Garage #" & ValInt(p(1)) & " is now nameless"
				End If
				Echo
			Else
				Echo "Invalid garage"
				Echo
			End If
		Case "rmgarage", "rmg"
			If ps = 0 Then
				Echo "Use: rmgarage <garage>"
				Echo
				Exit Sub
			End If
			
			If ValInt(p(1)) = 0 Then
				For i As Short = 1 To garages
					If LCase(garage(i).name) = LCase(p(1)) Then
						p(1) = Str(i)
						Exit For
					End If
				Next i
			End If
			
			If ValInt(p(1)) > garages Then
				Echo "There are only " & garages & " garages!"
			ElseIf ValInt(p(1)) <= 0 Then
				Echo "Invalid garage"
			ElseIf ValInt(p(1)) = 1 AndAlso garages = 1 Then
				Echo "Cannot remove the only garage"
			Else
				For i As Short = ValInt(p(1)) To garages - 1
					garage(i) = garage(i + 1)
				Next i
				garages -= 1
				SaveConfig
				Echo "Removed garage from list"
				Echo "Please note that any cars in the removed garage will remain"
				Echo "in the garage directory!"
			End If
			Echo
		Case "list", "ls", "lp", "lg", "lj", "l"
			Dim As Byte fil(0 To 15)	'Filter flags
			Dim thislist As String, acar As String, comma As Short
			Dim carcount As Short, found As Byte
			
			Select Case Mid(c, 2)
				Case "", "ist"
					If ps = 2 Then
						Dim g As Short
						g = GarageNumber(p(2))
						If g >= 0 AndAlso g <= 15 Then fil(g) = -1
					Else
						For i As Short = 0 To garages
							fil(i) = -1
						Next i
					End If
				Case "s" : fil(0) = -1
				Case "g", "p"
					For i As Short = 1 To garages
						If LCase(garage(i).name) <> "junkyard" Then fil(i) = -1
					Next i
				Case "j"
					Dim g As Byte
					g = GarageNumber("junkyard")
					If g Then
						fil(g) = -1
					Else
						Echo "No junkyard found"
						Echo
						Exit Sub
					End If
			End Select
						
			If p(1) = "" Then p(1) = "*"
			thislist = Resolve(p(1)) : carcount = 0
			Do
				comma = InStr(thislist, ",")
				If comma Then
					acar = Left(thislist, comma - 1)
					thislist = Mid(thislist, comma + 1)
				Else
					acar = thislist
					thislist = ""
				End If
				
				found = 0
				For j As Short = 0 To garages
					If fil(j) Then
						For i As Short = 1 To cars(j)
							If LCase(car(j, i).id) = LCase(acar) Then
								Echo car(j, i).carname & Space(30 - Len(car(j, i).carname)), 0
								Echo UCase(car(j, i).id), 0
								If car(j, i).numfiles <> 4 Then Echo "!  ", 0 Else Echo "   ", 0
								If j Then
									If Len(garage(j).name) Then
										Echo garage(j).name
									Else
										Echo "Garage", 0
										If garages > 1 Then
											Echo " #" & j
										Else
											Echo
										End If
									End If
								Else
									Echo "Stunts"
								End If
								carcount += 1							
								found = -1
								Exit For
							End If
						Next i
					End If
				Next j
			Loop Until Len(thislist) = 0
			If carcount > 1 Then
				Echo carcount & " cars found"
			ElseIf carcount = 1 Then
				Echo "1 car found"
			Else
				Echo "No cars found"
			End If
			Echo
		Case "park"
			Dim n As Short, t As String, tt As String, c As Short
			Dim g As Short
			
			If ps < 1 OrElse ps > 2 Then
				Echo "Use: park <carID> [<garage>]"
				Echo
			Else
				g = 1
				If ps = 2 Then
					g = GarageNumber(p(2))
					If g > garages Then
						Echo "Only " & garages & " garages available"
						Echo
						Exit Sub
					End If
				End If
				
				t = Resolve(p(1))
				Do
					c = InStr(t, ",")
					If c Then
						tt = Left(t, c - 1)
						t = Mid(t, c + 1)
					Else
						tt = t
						t = ""
					End If
					
					n = MoveCar(tt, 0, g)
					Select Case n
						Case 0 : Echo "Successfully parked " & tt
						Case 1 : Echo "Car not found: " & tt
						Case 2 : Echo "Some cars parked"
						Case 3 : Echo "Car is locked and cannot be parked: " & tt
						Case Else : Echo "Unknown error"
					End Select
				Loop Until Len(t) = 0
				Echo
			End If
		Case "stunts"
			If Len(1) = 0 Then
				Echo "Use: stunts <directory>"
				Echo
			ElseIf Open(p(1) & SLASH & "a123.tmp", For Output, As 1) Then
				Echo "Invalid path: " & p(1)
				Echo
			Else
				Close 1
				Kill p(1) & SLASH & "a123.tmp"
				garage(0).dir = p(1)
				SaveConfig
				For g As Byte = 0 To garages
					cars(g) = 0
				Next g
				garages = 1
				Init
				Echo "New Stunts directory is " & p(1)
				Echo
			End If
		Case "groups", "gs"
			Dim i As Short
			For i = 1 To groups
				Echo group(i).gname
			Next i
			Echo
			Echo i - 1 & " group", 0
			If i - 1 = 1 Then Echo Else Echo "s"
			Echo
		Case "group", "g"
			RunCommand "l #" & p(1)
		Case "removefrom", "rf"
			Dim thisgroup As Short, st As String, ss As String, nn As Short
			
			If ps <> 2 Then
				Echo "Use: removefrom/rf <group> <carid>"
				Echo
				Exit Sub
			End If
			
			For i As Short = 1 To groups
				If group(i).gname = LCase(p(1)) Then
					thisgroup = i
					Exit For
				End If
			Next i
			
			If thisgroup = 0 Then
				Echo "Group not found: " & p(1)
				Echo
				Exit Sub
			End If
			
			st = Resolve(p(2))
			Do
				nn = InStr(st, ",")
				If nn Then
					ss = Left(st, nn - 1)
					st = Mid(st, nn + 1)
				Else
					ss = st
					st = ""
				End If
				
				n = InStr(LCase(group(thisgroup).cars), LCase(ss))
				If Len(ss) <> 4 Then
					Echo "invalid car id: " & ss
				ElseIf n = 0 Then
					Echo "Car " & ss & " was not in that group"
				Else
					Dim As String b1, b2, temp
					b1 = LCase(Trim(group(thisgroup).cars))
					Do
						n = InStr(b1, ",")
						If n Then
							temp = RTrim(Left(b1, n - 1))
							b1 = LTrim(Mid(b1, n + 1))
						Else
							temp = RTrim(b1)
							b1 = ""
						End If
						
						If temp <> LCase(ss) Then
							If b2 = "" Then b2 = temp Else b2 &= "," & temp
						End If
					Loop Until Len(b1) = 0
					If Len(b2) Then
						group(thisgroup).cars = b2
						Echo "Removed " & ss & " from group " & p(1)
					Else
						For i As Short = thisgroup To groups - 1
							group(i) = group(i + 1)
						Next i
						groups -= 1
						Echo "Removed group " & p(1)
						Exit Do
					End If
				End If
			Loop Until Len(st) = 0
			Echo
			SaveConfig
		Case "delgroup", "rmgroup"
			If ps <> 1 Then
				Echo "Use: delgroup|rmgroup <group>"
				Echo
				Exit Sub
			End If
			
			If Left(p(1), 1) = "#" Then p(1) = Mid(p(1), 2)
			
			Dim q As Byte = 0
			
			For i As Short = 1 To groups
				If LCase(p(1)) = LCase(group(i).gname) Then
					q = -1
					For j As Short = i To groups - 1
						group(j) = group(j + 1)
					Next j
					groups -= 1
					SaveConfig
					Echo "Removed group " & p(1)
					Echo
					Exit For
				End If
			Next i
			
			If q = 0 Then
				Echo "Group not found: " & p(1)
				Echo
			End If
		Case "groupthese", "gt"
			If ps <> 1 Then
				Echo "Use: groupthese|gs <group>"
				Echo
				Exit Sub
			End If
			If cars(0) = 0 Then
				Echo "No cars to add to group"
				Echo
				Exit Sub
			End If
			For i As Short = 1 To cars(0)
				RunCommand "addto " & p(1) & " " & car(0, i).id
			Next i
			SaveConfig
		Case "only"
			If ps <> 1 Then
				Echo "Use: only <group>"
				Echo
				Exit Sub
			End If
			
			If Left(p(1), 1) = "#" Then p(1) = Mid(p(1), 2)
			
			Dim g As Short = 0
			
			For i As Short = 1 To groups
				If LCase(p(1)) = LCase(group(i).gname) Then
					g = i
					Exit For
				End If
			Next i
			
			If g = 0 Then
				Echo "Group not found: " & p(1)
				Echo
				Exit Sub
			End If
			
			'Move to garage cars that are not in group
			For i As Short = 1 To cars(0)
				If InStr(LCase(group(g).cars), LCase(car(0, i).id)) = 0 Then
					RunCommand "park " & car(0, i).id
					i -= 1
				End If
				If i = cars(0) Then Exit For
			Next i
			
			'Move to Stunts cars that are in group
			For i As Short = 1 To cars(1)
				If InStr(LCase(group(g).cars), LCase(car(1, i).id)) Then
					RunCommand "bring " & car(1, i).id
					i -= 1
				End If
				If i = cars(1) Then Exit For
			Next i
		Case "lock"
			If ps <> 1 Then
				Echo "Use: lock <carID>"
				Echo
				Exit Sub
			End If
			p(1) = LCase(Trim(p(1)))
			
			If InStr(p(1), ",") Then
				Echo "Only one car ID allowed for this command"
				Echo
				Exit Sub
			End If
			
			If Len(p(1)) <> 4 Then
				Echo "Invalid car ID: " & UCase(p(1))
				Echo
				Exit Sub
			End If
			If InStr(locked, p(1)) Then
				Echo "Car already locked: " & UCase(p(1))
				Echo
				Exit Sub
			End If
			If Len(locked) Then locked &= ","
			locked &= p(1)
			Echo "Locked " & UCase(p(1))
			Echo
			SaveConfig
		Case "unlock"
			If ps <> 1 Then
				Echo "Use: lock <carID>"
				Echo
				Exit Sub
			End If
			p(1) = LCase(Trim(p(1)))
			
			If InStr(p(1), ",") Then
				Echo "Only one car ID allowed for this command"
				Echo
				Exit Sub
			End If
			
			If Len(p(1)) <> 4 Or InStr(p(1), ",") <> 0 Then
				Echo "Invalid car ID: " & UCase(p(1))
				Echo
				Exit Sub
			End If
			Dim n As Short
			n = InStr(locked, p(1))
			If n = 0 Then
				Echo "Car already unlocked: " & UCase(p(1))
				Echo
				Exit Sub
			End If
			locked = Left(locked, n - 1) & Mid(locked, n + 4)
			n = InStr(locked, ",,")
			If n Then locked = Left(locked, n) & Mid(locked, n + 2)
			If Right(locked, 1) = "," Then locked = Left(locked, Len(locked) - 1)
			If Left(locked, 1) = "," Then locked = Mid(locked, 2)
			Echo "Unlocked car: " & UCase(p(1))
			Echo
			SaveConfig
		Case "locked"
			If Len(locked) Then
				Echo "Locked cars: " & locked
			Else
				Echo "No cars locked"
			End If
			Echo
		Case "version", "ver"
			Echo "Simple Garage v" & SGVERSION & ", released " & release_date & " - by Cas"
			Echo
		Case "resolve"
			Echo Resolve(p(1))
			Echo
		Case "help"
			If LCase(p(1)) = "filters" Then
				Echo "Most commands in which a car o more can be specified,"
				Echo "where you see a parameter like <car(s)> can use any of"
				Echo "the following:"
				Echo
				Echo "- A single car ID, e.g.: pmin"
				Echo "- A group preceeded by a # sign, e.g.: #mycars"
				Echo "- A filter such as: Porsche*"
				Echo "- A comma-separated list of any of the above: jagu,*n,#orig,fgto"
				Echo
				Echo "Comma-separated lists are called ad-hoc groups in contrast"
				Echo "to referenced groups, which are preceeded by a #."
				Echo "Filters can be of either of the forms:"
				Echo
				Echo "*xyz - ending in xyz"
				Echo "xyz* - beginning with xyz"
				Echo "*xyz* - containing xyz"
				Echo "xyz - also containing xyz (as long as length is not 4)"
				Echo
				Echo "Four characters in a row will be assumed to be a car ID"
				Echo
				Exit Sub
			End If
			
			Echo "addto <group> <car(s)> - Add car(s) to group"
			Echo "car <carID> - Display information about a car"
			Echo "cars - Display number of cars in Stunts & garage"
			Echo "delgroup|rmgroup <group> - Delete a group"
			Echo "dirs - Display current directories"
			Echo "exit|quit - Leave Simple Garage"
			Echo "group|g <group> - List cars in group"
			Echo "groups|gs - List groups"
			Echo "groupthese|gt <group> - Add all cars currently in Stunts to group"
			Echo "lg|lp <car(s)> - List cars among the ones in the garage"
			Echo "list|l <car(s)> - List cars among all available"
			Echo "lock <carID> - Lock car"
			Echo "locked - List locked car IDs"
			Echo "ls <car(s)> - List cars among those ready in Stunts"
			Echo "only <group> - Bring all cars in group and park all others"
			Echo "park <car(s)> [garage] - Park car(s) in garage"
			Echo "removefrom|rf <group> <car(s)> - Remove car(s) from group"
			Echo "retrieve|bring <car(s)> [garage] - Bring car(s) back to Stunts"
			Echo "unlock <carID> - Unlock car"
			Echo "version|ver - Display program version"
			Echo
			Echo "Type 'help filters' for more information on car lists"
			Echo
		Case "exit", "quit" : End
		Case Else
			Echo "Uknown command: " & c
			Echo
	End Select
End Sub


Sub MyInput (s As String, maxlength As Short)
	Dim akey As String, t As String, cpos As Short = 1
	Dim As Short startx, starty
	Dim update As Byte = -1, lastn As Byte
	
	startx = Pos(0)
	starty = CSRLin
	
	Do
		If update Then
			Locate starty, startx
			Print t & Space(80 - Len(t) - startx);
			Line (8 * (startx - 2 + cpos), 16 * starty - 3)- Step (7, 0)
			update = 0
		End If
		
		akey = InKey
		Sleep 10
		
		Select Case akey
			Case " " To Chr(126)
				If Len(t) < maxlength Then
					t = Left(t, cpos - 1) & akey & Mid(t, cpos)
					cpos += 1
					update = -1
				End If
			Case Chr(8)
				If cpos > 1 Then
					t = Left(t, cpos - 2) & Mid(t, cpos)
					cpos -= 1
					update = -1
				End If
			Case Chr(255, 77)
				If cpos < Len(t) + 1 Then
					cpos += 1
					update = -1
				End If
			Case Chr(255, 75)
				If cpos > 1 Then
					cpos -= 1
					update = -1
				End If
			Case Chr(255, 83)
				If cpos <= Len(t) Then
					t = Left(t, cpos - 1) & Mid(t, cpos + 1)
					update = -1
				End If
			Case Chr(255, 71)
				cpos = 1
				update = -1
			Case Chr(255, 79)
				cpos = Len(t) + 1
				update = -1
			Case Chr(255, 72)
				If lastn < 10 Then
					lastn += 1
					t = lastcommand(lastn)
					cpos = Len(t) + 1
					update = -1
				End If
			Case Chr(255, 80)
				If lastn > 0 Then
					lastn -= 1
					If lastn Then
						t = lastcommand(lastn)
					Else
						t = ""
					End If
					cpos = Len(t) + 1
					update = -1
				End If
			Case Chr(13)
				s = t
				Exit Do
			Case Chr(27)
				t = ""
				cpos = 1
				update = -1
			Case Chr(255) & "k"
				s = "exit"
				Exit Do
		End Select
	Loop
	
	Locate starty, startx : Print t & "  "
End Sub


release_date = __DATE_ISO__
copyright_notice = "Copyright (C) 2022 - Lucas Pedrosa" & Chr(10) & _
	"This program is free software distributed under the GPLv3" & Chr(10) & _
	"See license.txt for details"

If Command = "/?" Or LCase(Command) = "--help" Then
	Print "Simple Garage " & SGVERSION & ", released " & release_date
	Print "Garage manager program for Stunts"
	Print copyright_notice
	Print
	Print "Use: sgar <command>"
	Print "to execute individual commands"
	Print "Or: sgar"
	Print "to access the GUI console"
	Print
	End
End If

If Len(Command) Then
	Dim comline As String, n As Short
	
	comline = Command
	Do
		n = InStr(comline, "%")
		If n Then Mid(comline, n, 1) = "*"
	Loop Until n = 0
	Do
		n = InStr(comline, "@")
		If n Then Mid(comline, n, 1) = "#"
	Loop Until n = 0
	
	Init
	RunCommand comline
	End
End If


ScreenRes 640, 480
Width 80, 30

WindowTitle "Simple Garage " & SGVERSION

Dim s As String

Color 15
Print "Simple Garage " & SGVERSION & ", released " & release_date
Color 7
Print copyright_notice
Print
Color 15

Init

Do
	Color 10 : Print "@>";
	Color 15 : MyInput s, 70
	Color 7 : RunCommand s

	For i As Short = 10 To 2 Step -1
		lastcommand(i) = lastcommand(i - 1)
	Next i
	lastcommand(1) = s
Loop
